(* DONT_TOUCH = "TRUE" *)
module IIR_2_core_pipline #(
    parameter datin_width = 16,
    parameter daout_width = 16,
    parameter index_bit = 12,
    parameter index_max = 10,
    parameter ds_rate = 10
)(
    input clk,
    input rst_n,
    input en_flag,
    output reg busy,
    output next_en_flag,

    input [datin_width-1:0] datin_bram_r_data,
    output datin_bram_r_en,
    output [index_bit-1:0] datin_bram_r_addr,

    output [daout_width-1:0] daout_bram_w_data,
    output daout_bram_w_we,
    output daout_bram_w_en,
    output [index_bit-1:0] daout_bram_w_addr
);

// 状态定义
localparam IDLE  = 3'd0;
localparam READ  = 3'd1;
localparam READ_WAIT = 3'd2;
localparam CALC1 = 3'd3;
localparam CALC2 = 3'd4;
localparam WRITE = 3'd5;

reg [2:0] state;
reg [index_bit-1:0] current_index;

reg [datin_width-1:0] datin_reg;
reg [datin_width*6-1:0] cache_reg;

reg [datin_width-1:0] stage1_out;
reg [datin_width-1:0] stage2_out;

//cache bram信号
wire [datin_width*6-1:0] cache_bram_w_data;
wire cache_bram_w_we;
wire cache_bram_w_en;
wire [index_bit-1:0] cache_bram_w_addr;

wire [datin_width*6-1:0] cache_bram_r_data;
wire cache_bram_r_we;
wire cache_bram_r_en;
wire [index_bit-1:0] cache_bram_r_addr;

// 控制信号
assign datin_bram_r_addr = current_index;
assign datin_bram_r_en   = (state == READ);

assign cache_bram_r_addr = current_index;
assign cache_bram_r_en   = (state == READ);
assign cache_bram_r_we   = 1'b0;

assign daout_bram_w_addr = current_index;
assign daout_bram_w_data = stage2_out;
assign daout_bram_w_en   = (state == WRITE);
assign daout_bram_w_we   = (state == WRITE);

assign cache_bram_w_addr = current_index;
assign cache_bram_w_data = {
    cache_reg[5*datin_width-1 -: datin_width], // Yin2_2
    stage2_out,                                // Yin1_2
    cache_reg[3*datin_width-1 -: datin_width], // Yin2_1
    stage1_out,                                // Yin1_1
    cache_reg[1*datin_width-1 -: datin_width], // Xin2_1
    datin_reg                                  // Xin1_1
};
assign cache_bram_w_en = (state == WRITE);
assign cache_bram_w_we = (state == WRITE);
reg [clogb2(ds_rate)-1:0] ds_counter;
reg next_en_flag_r, next_en_flag_d;
assign next_en_flag = next_en_flag_r && ~next_en_flag_d;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        ds_counter <= 0;
        next_en_flag_r <= 1'b0;
        next_en_flag_d <= 1'b0;
    end else if (en_flag) begin
        if (ds_counter == ds_rate - 1) begin
            ds_counter <= 0;
            next_en_flag_r <= 1'b1;
        end else begin
            ds_counter <= ds_counter + 1;
            next_en_flag_r <= 1'b0;
        end
        next_en_flag_d <= next_en_flag_r;
    end else begin
        next_en_flag_r <= 1'b0;
        next_en_flag_d <= 1'b0;
    end
end

// 状态机流程
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= IDLE;
        current_index <= 0;
        busy <= 1'b0;
    end else begin
        case (state)
            IDLE: begin //0
                if (en_flag) begin
                    current_index <= 0;
                    state <= READ;
                    busy <= 1'b1;
                end
            end

            READ: begin //1
                state <= READ_WAIT;
            end

            READ_WAIT:begin //2
                datin_reg <= datin_bram_r_data;
                cache_reg <= cache_bram_r_data;
                state <= CALC1;
            end

            CALC1: begin //3
                state <= CALC2;
            end

            CALC2: begin //4
                state <= WRITE;
            end

            WRITE: begin //5
                if (current_index >= index_max - 1) begin
                    state <= IDLE;
                    busy <= 1'b0;
                end else begin
                    current_index <= current_index + 1;
                    state <= READ;
                end
            end
        endcase
    end
end

// 二级 IIR 实例化
wire [datin_width-1:0] stage1_result, stage2_result;

IIR_single_stage_dsp #(
    .datin_width(datin_width),
    .coeffct_width(20),//dsp最高给25，量化为n-2
    .daout_width(daout_width),
    .b0(1636),
    .b1(-531),
    .b2(1636),
    .a0(131072),
    .a1(-186275),
    .a2(67915)
)u_IIR1 (
    .Xin (datin_reg),
    .Xin1(cache_reg[1*datin_width-1 -: datin_width]),
    .Xin2(cache_reg[2*datin_width-1 -: datin_width]),
    .Yin1(cache_reg[3*datin_width-1 -: datin_width]),
    .Yin2(cache_reg[4*datin_width-1 -: datin_width]),
    .Yout(stage1_result)
);
IIR_single_stage_dsp #(
    .datin_width(datin_width),
    .coeffct_width(20),
    .daout_width(daout_width),
    .b0(131072),
    .b1(-204435),
    .b2(131072),
    .a0(131072),
    .a1(-224081),
    .a2(105456)
)u_IIR2 (
    .Xin (stage1_out),
    .Xin1(cache_reg[3*datin_width-1 -: datin_width]),
    .Xin2(cache_reg[4*datin_width-1 -: datin_width]),
    .Yin1(cache_reg[5*datin_width-1 -: datin_width]),
    .Yin2(cache_reg[6*datin_width-1 -: datin_width]),
    .Yout(stage2_result)
);

// Pipeline寄存器
always @(posedge clk) begin
    if (state == CALC1) stage1_out <= stage1_result;
    if (state == CALC2) stage2_out <= stage2_result;
end

// cache_bram interface
cache_bram u_cache_bram (
    .clka(clk),    // input wire clka 写
    .ena(cache_bram_w_en),      // input wire ena
    .wea(cache_bram_w_we),      // input wire [0 : 0] wea
    .addra(cache_bram_w_addr),  // input wire [10 : 0] addra
    .dina(cache_bram_w_data),    // input wire [95 : 0] dina

    .clkb(clk),    // input wire clkb 读
    .enb(cache_bram_r_en),      // input wire enb
    .addrb(cache_bram_r_addr),  // input wire [10 : 0] addrb
    .doutb(cache_bram_r_data)  // output wire [95 : 0] doutb
);

function integer clogb2(input integer depth);
  integer tmp;
  begin
    tmp = depth;
    for (clogb2 = 0; tmp > 0; clogb2 = clogb2 + 1) 
      tmp = tmp >> 1;                          
  end
endfunction

endmodule
